/**
 * Copyright 2009-2010 - YangJiandong(chunquedong)
 * 
 * This file is part of ChunMap project
 * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE(Version >=3)
 * 你可以自由复制、传播本项目的下载包文件，但必须保持其完整性。
 * 我们不用对使用中的风险及由此造成的损失承担任何责任。
 * 详细情况请见《ChunMap许可协议》。

 * 想了解更多有关ChunMap的信息，请访问http://code.google.com/p/chunmap/
 */
package chunmap.model.geom;

import java.util.ArrayList;
import java.util.List;

import chunmap.model.algorithm.LineAlgorithm;
import chunmap.model.algorithm.LinearReference;
import chunmap.model.coord.CompactedCoordinateArray;
import chunmap.model.coord.CoordinateSeq;
import chunmap.model.coord.Position;
import chunmap.model.coord.Transform;
import chunmap.model.elem.Envelope;
import chunmap.model.elem.EnvelopeBuilder;
import chunmap.model.elem.LineSegment;
import chunmap.model.elem.PointLineBag;

/**
 * 线串
 * 
 * @author chunquedong
 * 
 */
public class LineString extends AbstractGeometry {

	protected CoordinateSeq points;

	public LineString(List<Position> pointList) {
		this(pointList.toArray(new Position[0]));
	}

	public LineString(Position[] pointList) {
		points = new CompactedCoordinateArray(pointList);
	}
	
	public LineString(CoordinateSeq points) {
		this.points=points;
	}

	//------------------------------------------------------------------------getter

	public Position getPoint(int index) {
		return points.getCoordinate(index);
	}
	
	public int size() {
		return points.size();
	}

	public CoordinateSeq getPoints()
    {
        return points;
    }
	
	public boolean isClosed()
    {
        return firstPoint().equals(lastPoint());
    }

	public Position firstPoint() {
		return getPoint(0);
	}

	public Position lastPoint() {
		return getPoint(size() - 1);
	}

	public Ring toLinearRing() {
		return new Ring(points);
	}

	//------------------------------------------------------------------------geometry

	@Override
	protected Envelope calculateEnvelop() {
		EnvelopeBuilder env =new EnvelopeBuilder();
		for (Position p : points) {
			env.extendEnvelop(p);
		}
		return env.toEnvelop();
	}

	@Override
	public GeometryType getGeometryType() {
		return GeometryType.LineString;
	}

	@Override
	public String toString() {
		return "LINESTRING" + toMyString();
	}

	public String toMyString() {
		StringBuilder str = new StringBuilder();
		str.append("(");
		for (Position p : points) {
			str.append(p.getX() + " " + p.getY() + ",");
		}
		str.deleteCharAt(str.lastIndexOf(","));
		str.append(")");
		return str.toString();
	}

	@Override
	public Geometry getBoundary() {
		List<GeoPoint> bs = new ArrayList<GeoPoint>();
		bs.add(new GeoPoint(firstPoint()));
		bs.add(new GeoPoint(lastPoint()));

		return new MultiPoint(bs);
	}

	@Override
	public GeometryCollection toGeometryCollection() {
		List<LineString> gs = new ArrayList<LineString>();
		gs.add(this);
		return new MultiLineString(gs);
	}

	@Override
	protected boolean isSimple() {
		return LineAlgorithm.isSimple(this.getPoints());
	}


	/**
	 * 坐标变换
	 */
	@Override
	public Geometry transform(Transform transf) {
		List<Position> pointList = new ArrayList<Position>();
		for (Position p : points) {
			pointList.add(transf.convert(p));
		}
		return new LineString(pointList);
	}
	//------------------------------------------------------------------------system
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((points == null) ? 0 : points.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		LineString other = (LineString) obj;
		if (points == null) {
			if (other.points != null)
				return false;
		} else if (!points.equals(other.points))
			return false;
		return true;
	}
	
	//------------------------------------------------------------------------util

    public LineSegment getLineSegment(int i)
    {
        return new LineSegment(getPoint(i), getPoint(i+1));
    }
    public boolean onLineString(Position p)
    {
        if (!this.getEnvelop().contain(p))
        {
            return false;
        }
        return LineAlgorithm.onLineString(points, p);
    }
    public PointLineBag intersection(LineString l2)
    {
        if (!this.getEnvelop().hasIntersect(l2.getEnvelop()))
        {
            return new PointLineBag();
        }
        return LineAlgorithm.intersection(this.points, l2.points);
    }
    public boolean hasIntersection(LineString l2)
    {
        if (!this.getEnvelop().hasIntersect(l2.getEnvelop()))
        {
            return false;
        }
        return LineAlgorithm.hasIntersection(this.points, l2.points);
    }
    public boolean containLineString(LineString l2)
    {
        if (!this.getEnvelop().hasIntersect(l2.getEnvelop()))
        {
            return false;
        }
        return LineAlgorithm.containLineString(this.points, l2.points);
    }
    public double getLength()
    {
        return LinearReference.getLength(points);
    }
}